using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Joy.Erp.BasicArchives.Asns;
using Joy.Erp.BasicArchives.Core;
using Joy.Erp.BasicArchives.ItemMasters;
using Joy.Erp.BasicArchives.Procurements;
using Joy.Erp.BasicArchives.Production;
using Joy.Erp.BasicArchives.Receipt;
using Joy.Erp.Ufida.ItemMasters;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;

namespace Joy.Erp.Ufida.Pmc
{
    [Authorize]
    public class MaterialManagementPlatformAppService
        : UfidaAppService,
            IMaterialManagementPlatformAppService
    {
        protected IProductionOrderRepository ProductionOrderRepository =>
            LazyServiceProvider.LazyGetRequiredService<IProductionOrderRepository>();
        protected ICurrentStockUfidaAppService CurrentStockUfidaAppService =>
            LazyServiceProvider.LazyGetRequiredService<ICurrentStockUfidaAppService>();
        protected IReceivementRepository ReceivementRepository =>
            LazyServiceProvider.LazyGetRequiredService<IReceivementRepository>();
        protected IAsnRepository AsnRepository =>
            LazyServiceProvider.LazyGetRequiredService<IAsnRepository>();
        protected IPurchaseOrderRepository PurchaseOrderRepository =>
            LazyServiceProvider.LazyGetRequiredService<IPurchaseOrderRepository>();
        protected IPurchaseRequisitionRepository PurchaseRequisitionRepository =>
            LazyServiceProvider.LazyGetRequiredService<IPurchaseRequisitionRepository>();
        protected IMaterialBalanceCacheService MaterialBalanceCacheService =>
            LazyServiceProvider.LazyGetRequiredService<IMaterialBalanceCacheService>();

        public async Task<bool> LoadSupplyDemandDataAsync(LoadSupplyDemandDataInput input)
        {
            // 检查缓存是否存在
            if (await MaterialBalanceCacheService.ExistsAsync(input.CutoffDate))
            {
                return true;
            }

            // 1. 获取所有需求数据
            var demandRecords = await GetAllDemandRecordsAsync(input.CutoffDate);

            // 2. 获取所有供应数据
            var supplyRecords = await GetAllSupplyRecordsAsync(input.CutoffDate);

            // 3. 合并所有供需记录
            var allRecords = new List<SupplyDemandRecordDto>();
            allRecords.AddRange(demandRecords);
            allRecords.AddRange(supplyRecords);

            // 4. 存入缓存
            await MaterialBalanceCacheService.SetSupplyDemandRecordsAsync(
                input.CutoffDate,
                allRecords
            );

            return true;
        }

        public async Task<
            PagedResultDto<MaterialBalanceAnalysisDto>
        > GetMaterialBalanceAnalysisAsync(GetMaterialBalanceInput input)
        {
            // 检查缓存是否存在或需要强制刷新
            if (
                input.ForceRefresh
                || !await MaterialBalanceCacheService.ExistsAsync(input.CutoffDate)
            )
            {
                await LoadSupplyDemandDataAsync(
                    new LoadSupplyDemandDataInput { CutoffDate = input.CutoffDate }
                );
            }

            // 1. 从缓存获取所有供需记录
            var allRecords = await MaterialBalanceCacheService.GetSupplyDemandRecordsAsync(
                input.CutoffDate
            );

            if (allRecords == null || allRecords.Count == 0)
            {
                return new PagedResultDto<MaterialBalanceAnalysisDto>(
                    0,
                    new List<MaterialBalanceAnalysisDto>()
                );
            }

            // 2. 按料品分组
            var groupedByItem = allRecords
                .Where(r => !string.IsNullOrWhiteSpace(r.ItemCode))
                .GroupBy(r => r.ItemCode)
                .Select(g => new
                {
                    ItemCode = g.Key,
                    Records = g.ToList(),
                    FirstRecord = g.First(),
                })
                .ToList();

            // 3. 创建字典以提高查询效率
            var recordsDict = groupedByItem.ToDictionary(g => g.ItemCode, g => g.Records);

            // 4. 创建料品基本信息列表
            var allMaterials = groupedByItem
                .Select(g => new MaterialBalanceAnalysisDto
                {
                    ItemMasterId = g.FirstRecord.SourceId ?? Guid.Empty,
                    ItemCode = g.ItemCode,
                    ItemName = g.FirstRecord.ItemName,
                    ItemSpec = g.FirstRecord.ItemSpec,
                })
                .ToList();

            // 5. 应用筛选条件
            var filteredMaterials = allMaterials;

            // 5.1 按料品编号筛选
            if (!string.IsNullOrWhiteSpace(input.ItemCode))
            {
                filteredMaterials = filteredMaterials
                    .Where(x => x.ItemCode == input.ItemCode)
                    .ToList();
            }

            // 5.2 按料品ID集合筛选
            if (input.ItemMasterIds != null && input.ItemMasterIds.Length > 0)
            {
                filteredMaterials = filteredMaterials
                    .Where(x => input.ItemMasterIds.Contains(x.ItemMasterId))
                    .ToList();
            }

            // 6. 计算总数
            var totalCount = filteredMaterials.Count;

            // 7. 应用分页（只对当前页的数据进行详细计算）
            var pagedMaterials = filteredMaterials
                .Skip(input.SkipCount)
                .Take(input.MaxResultCount)
                .ToList();

            // 8. 只计算当前页料品的详细数据
            foreach (var material in pagedMaterials)
            {
                // 从字典中获取该料品的所有供需记录，效率更高
                var materialRecords = recordsDict.TryGetValue(material.ItemCode, out var records)
                    ? records
                    : new List<SupplyDemandRecordDto>();

                // 计算汇总数据
                CalculateMaterialBalance(material, materialRecords, input.CutoffDate);
            }

            // 9. 应用缺料筛选（如果需要）
            // 只显示期内缺料的料品（期内缺料最紧急）
            if (input.OnlyShowShortage)
            {
                pagedMaterials = pagedMaterials.Where(x => x.IsWithinPeriodShortage).ToList();
            }

            return new PagedResultDto<MaterialBalanceAnalysisDto>(totalCount, pagedMaterials);
        }

        public async Task ClearCacheAsync()
        {
            await MaterialBalanceCacheService.ClearAsync();
        }

        /// <summary>
        /// 计算料品的供需平衡数据
        /// </summary>
        private void CalculateMaterialBalance(
            MaterialBalanceAnalysisDto material,
            List<SupplyDemandRecordDto> records,
            DateTime cutoffDate
        )
        {
            material.SupplyDemandRecords = records;
            material.DailyBalances = new List<DailyBalanceDto>();

            // 计算初始库存、总需求、总供应
            foreach (var record in records)
            {
                if (record.Type == SupplyDemandType.Demand)
                {
                    material.TotalDemandQuantity += record.PendingQuantity;
                }
                else if (record.Type == SupplyDemandType.Supply)
                {
                    if (record.DocType == "现存量")
                    {
                        material.InitialStock += record.Quantity;
                    }
                    else
                    {
                        material.TotalSupplyQuantity += record.PendingQuantity;
                    }
                }
            }

            // 计算每日供需平衡
            CalculateDailyBalance(material, cutoffDate);

            // 计算期末预计结存
            material.ProjectedEndingStock =
                material.InitialStock + material.TotalSupplyQuantity - material.TotalDemandQuantity;
        }

        /// <summary>
        /// 计算每日供需平衡
        /// </summary>
        private void CalculateDailyBalance(MaterialBalanceAnalysisDto material, DateTime cutoffDate)
        {
            var today = DateTime.Now.Date;

            // 1. 生成从今天到截止日期的完整日期序列
            var allDates = new List<DateTime>();
            for (var date = today; date <= cutoffDate; date = date.AddDays(1))
            {
                allDates.Add(date);
            }

            // 如果没有日期范围，直接返回
            if (!allDates.Any())
            {
                return;
            }

            // 2. 初始化变量
            decimal currentStock = material.InitialStock;
            decimal withinPeriodStock = material.InitialStock;
            // 期初现存量算在期内总供应中
            decimal withinPeriodTotalSupply = material.InitialStock;
            decimal withinPeriodTotalDemand = 0;
            decimal outOfPeriodTotalSupply = 0;
            decimal outOfPeriodTotalDemand = 0;
            bool isWithinPeriodShortage = false;
            decimal withinPeriodMaxShortageQuantity = 0;

            // 3. 计算今天之前的供需（汇总到今天）
            var beforeTodayDemand = 0m;
            var beforeTodaySupply = 0m;

            foreach (var record in material.SupplyDemandRecords)
            {
                if (string.IsNullOrWhiteSpace(record.BusinessDate))
                    continue;

                var businessDate = DateTime.Parse(record.BusinessDate).Date;

                if (businessDate < today)
                {
                    if (record.Type == SupplyDemandType.Demand)
                    {
                        beforeTodayDemand += record.PendingQuantity;
                    }
                    else if (record.Type == SupplyDemandType.Supply && record.DocType != "现存量")
                    {
                        beforeTodaySupply += record.PendingQuantity;
                    }
                }
            }

            // 4. 遍历每一天（从今天到截止日期）
            foreach (var date in allDates)
            {
                var dateStr = date.ToString("yyyy-MM-dd");
                var dailyBalance = new DailyBalanceDto
                {
                    Date = dateStr,
                    CurrentStock = currentStock,
                    DemandQuantity = 0,
                    SupplyQuantity = 0,
                };

                // 如果是今天，加上今天之前的供需
                if (date == today)
                {
                    dailyBalance.DemandQuantity = beforeTodayDemand;
                    dailyBalance.SupplyQuantity = beforeTodaySupply;
                }

                // 汇总当日的需求和供应
                var dailyRecords = material
                    .SupplyDemandRecords.Where(x =>
                        !string.IsNullOrWhiteSpace(x.BusinessDate)
                        && DateTime.Parse(x.BusinessDate).Date == date
                    )
                    .ToList();

                foreach (var record in dailyRecords)
                {
                    if (record.Type == SupplyDemandType.Demand)
                    {
                        dailyBalance.DemandQuantity += record.PendingQuantity;
                    }
                    else if (record.Type == SupplyDemandType.Supply && record.DocType != "现存量")
                    {
                        dailyBalance.SupplyQuantity += record.PendingQuantity;
                    }
                }

                // 累计期内统计
                withinPeriodTotalDemand += dailyBalance.DemandQuantity;
                withinPeriodTotalSupply += dailyBalance.SupplyQuantity;

                // 计算期末结存
                dailyBalance.ProjectedEndingStock =
                    dailyBalance.CurrentStock
                    + dailyBalance.SupplyQuantity
                    - dailyBalance.DemandQuantity;

                // 判断是否缺料
                if (dailyBalance.ProjectedEndingStock < 0)
                {
                    dailyBalance.IsShortage = true;
                    dailyBalance.ShortageQuantity = Math.Abs(dailyBalance.ProjectedEndingStock);

                    // 标记期内缺料，并记录最大缺料数量
                    isWithinPeriodShortage = true;
                    if (dailyBalance.ShortageQuantity > withinPeriodMaxShortageQuantity)
                    {
                        withinPeriodMaxShortageQuantity = dailyBalance.ShortageQuantity.Value;
                    }
                }

                // 更新期内结存
                withinPeriodStock = dailyBalance.ProjectedEndingStock;

                material.DailyBalances.Add(dailyBalance);

                // 更新下一天的期初库存
                currentStock = dailyBalance.ProjectedEndingStock;
            }

            // 5. 计算期外的供需（截止日期之后）
            foreach (var record in material.SupplyDemandRecords)
            {
                if (string.IsNullOrWhiteSpace(record.BusinessDate))
                    continue;

                var businessDate = DateTime.Parse(record.BusinessDate).Date;

                if (businessDate > cutoffDate)
                {
                    if (record.Type == SupplyDemandType.Demand)
                    {
                        outOfPeriodTotalDemand += record.PendingQuantity;
                    }
                    else if (record.Type == SupplyDemandType.Supply && record.DocType != "现存量")
                    {
                        outOfPeriodTotalSupply += record.PendingQuantity;
                    }
                }
            }

            // 6. 处理期内结存对期外供需的影响
            // 如果期内结存 > 0，算在期外供应里
            // 如果期内结存 < 0，取绝对值算在期外需求里
            if (withinPeriodStock > 0)
            {
                outOfPeriodTotalSupply += withinPeriodStock;
            }
            else if (withinPeriodStock < 0)
            {
                outOfPeriodTotalDemand += Math.Abs(withinPeriodStock);
            }

            // 7. 计算期外是否缺料和期外结存
            // 期外结存 = 期外总供应 - 期外总需求（期内结存已经处理到期外供需中了）
            var outOfPeriodProjectedStock = outOfPeriodTotalSupply - outOfPeriodTotalDemand;
            material.IsOutOfPeriodShortage = outOfPeriodProjectedStock < 0;
            if (material.IsOutOfPeriodShortage)
            {
                material.OutOfPeriodMaxShortageQuantity = Math.Abs(outOfPeriodProjectedStock);
            }

            // 8. 将期内期外汇总数据赋值给料品级别的 DTO
            material.WithinPeriodStock = withinPeriodStock;
            material.WithinPeriodTotalSupply = withinPeriodTotalSupply;
            material.WithinPeriodTotalDemand = withinPeriodTotalDemand;
            material.OutOfPeriodStock = outOfPeriodProjectedStock; // 使用计算出的期外结存
            material.OutOfPeriodTotalSupply = outOfPeriodTotalSupply;
            material.OutOfPeriodTotalDemand = outOfPeriodTotalDemand;

            // 9. 设置期内缺料标志
            material.IsWithinPeriodShortage = isWithinPeriodShortage;
            if (isWithinPeriodShortage)
            {
                material.WithinPeriodMaxShortageQuantity = withinPeriodMaxShortageQuantity;
            }
        }

        /// <summary>
        /// 转换单据状态为中文
        /// </summary>
        private string GetDocumentStatusDisplayName(DocumentStatus status)
        {
            return status switch
            {
                DocumentStatus.Open => "开立",
                DocumentStatus.Waiting => "提交",
                DocumentStatus.Approved => "已审核",
                DocumentStatus.Closed => "已关闭",
                _ => status.ToString(),
            };
        }

        /// <summary>
        /// 转换生产状态为中文
        /// </summary>
        private string GetProductionStatusDisplayName(ProductionStatus status)
        {
            return status switch
            {
                ProductionStatus.Created => "未开工",
                ProductionStatus.Started => "开工",
                ProductionStatus.Completed => "完工",
                ProductionStatus.Closed => "关闭",
                _ => status.ToString(),
            };
        }

        /// <summary>
        /// 获取所有需求数据
        /// </summary>
        private async Task<List<SupplyDemandRecordDto>> GetAllDemandRecordsAsync(
            DateTime cutoffDate
        )
        {
            var demandRecords = new List<SupplyDemandRecordDto>();

            // 获取生产订单的子项（备料）作为需求
            var productionOrders = await ProductionOrderRepository.GetListAsync(
                x =>
                    x.IsDeleted == false
                    && (
                        x.ProductionStatus == ProductionStatus.Created
                        || x.ProductionStatus == ProductionStatus.Started
                    ),
                true
            );

            foreach (var porder in productionOrders)
            {
                foreach (var child in porder.Children)
                {
                    // 只考虑采购件
                    if (child.ItemMaster.ItemFormAttribute != 9)
                        continue;

                    // 备料已全部领取的不计入需求
                    var pendingQuantity = Math.Max(0, child.Quantity - child.IssuedQuantity);
                    if (pendingQuantity == 0)
                        continue;

                    var demandRecord = new SupplyDemandRecordDto
                    {
                        Type = SupplyDemandType.Demand,
                        DocNo = porder.Code,
                        DocType = porder.OrderType?.DisplayName ?? "生产订单",
                        DocTypeName = porder.OrderType?.DisplayName ?? "生产订单",
                        BusinessDate = porder.StartDate.ToString("yyyy-MM-dd"),
                        Quantity = child.Quantity,
                        CompletedQuantity = child.IssuedQuantity,
                        PendingQuantity = pendingQuantity,
                        Status = GetProductionStatusDisplayName(porder.ProductionStatus),
                        Seiban = porder.Seiban,
                        DemandCode = porder.DemandCode,
                        Remark = $"产品：{porder.ItemName}",
                        SourceId = child.ItemMasterId,
                        SourceLineId = child.Id,
                        ItemCode = child.ItemCode,
                        ItemName = child.ItemName,
                        ItemSpec = child.ItemSpec,
                    };

                    demandRecords.Add(demandRecord);
                }
            }

            return demandRecords;
        }

        /// <summary>
        /// 获取所有供应数据
        /// </summary>
        private async Task<List<SupplyDemandRecordDto>> GetAllSupplyRecordsAsync(
            DateTime cutoffDate
        )
        {
            var supplyRecords = new List<SupplyDemandRecordDto>();

            // 1. 现存量
            var currentStockLines = await CurrentStockUfidaAppService.GetAllAsync();
            foreach (var stock in currentStockLines)
            {
                supplyRecords.Add(
                    new SupplyDemandRecordDto
                    {
                        Type = SupplyDemandType.Supply,
                        DocNo = stock.ID.ToString(),
                        DocType = "现存量",
                        DocTypeName = "现存量",
                        BusinessDate = DateTime.Now.Date.ToString("yyyy-MM-dd"),
                        Quantity = stock.Qty,
                        CompletedQuantity = 0,
                        PendingQuantity = stock.Qty,
                        Status = "可用",
                        SourceId = Guid.Empty,
                        ItemCode = stock.ItemCode,
                        ItemName = stock.ItemName,
                        ItemSpec = stock.ItemSpec,
                    }
                );
            }

            // 2. 收货单
            var receivementLines = await ReceivementRepository.GetChildrenPagedListAsync(
                x =>
                    x.Document.IsDeleted == false
                    && (
                        x.Document.DocumentStatus == DocumentStatus.Open
                        || x.Document.DocumentStatus == DocumentStatus.Waiting
                    ),
                0,
                int.MaxValue,
                null,
                true
            );

            foreach (var line in receivementLines)
            {
                supplyRecords.Add(
                    new SupplyDemandRecordDto
                    {
                        Type = SupplyDemandType.Supply,
                        DocNo = line.Document.Code,
                        DocType = "收货单",
                        DocTypeName = "收货单",
                        BusinessDate = line.Document.BusinessDate.ToString("yyyy-MM-dd"),
                        Quantity = line.Quantity,
                        CompletedQuantity = 0,
                        PendingQuantity = line.Quantity,
                        Status = GetDocumentStatusDisplayName(line.Document.DocumentStatus),
                        SourceId = line.ItemMasterId,
                        SourceLineId = line.Id,
                        ItemCode = line.ItemCode,
                        ItemName = line.ItemName,
                        ItemSpec = line.ItemSpec,
                    }
                );
            }

            // 3. ASN单
            var asnLines = await AsnRepository.GetChildrenPagedListAsync(
                x =>
                    x.Document.IsDeleted == false
                    && x.Document.ArriveDate.HasValue
                    && x.Document.DocumentStatus != DocumentStatus.Closed
                    && x.ReceivedQuantity
                        < x.Quantity // 只统计未完成的数量
                ,
                0,
                int.MaxValue,
                null,
                true
            );

            foreach (var line in asnLines)
            {
                supplyRecords.Add(
                    new SupplyDemandRecordDto
                    {
                        Type = SupplyDemandType.Supply,
                        DocNo = line.Document.Code,
                        DocType = "ASN单",
                        DocTypeName = "ASN单",
                        BusinessDate = line.Document.ArriveDate.Value.ToString("yyyy-MM-dd"),
                        Quantity = line.Quantity,
                        CompletedQuantity = line.ReceivedQuantity,
                        PendingQuantity = line.Quantity - line.ReceivedQuantity,
                        Status = GetDocumentStatusDisplayName(line.Document.DocumentStatus),
                        SourceId = line.ItemMasterId,
                        SourceLineId = line.Id,
                        ItemCode = line.ItemCode,
                        ItemName = line.ItemName,
                        ItemSpec = line.ItemSpec,
                    }
                );
            }

            // 4. 采购订单
            var purchaseOrderLines = await PurchaseOrderRepository.GetChildrenPagedListAsync(
                x =>
                    x.Document.IsDeleted == false
                    && x.DeliveryDate.HasValue
                    && x.Document.DocumentStatus != DocumentStatus.Closed
                    && x.RemainingQuantity
                        > 0 // 只统计未完成的数量
                ,
                0,
                int.MaxValue,
                null,
                true
            );

            foreach (var line in purchaseOrderLines)
            {
                var businessDate =
                    line.NewDeliveryDate
                    ?? line.SupplierDeliveryDate
                    ?? line.RequireDeliveryDate
                    ?? line.DeliveryDate.Value;

                supplyRecords.Add(
                    new SupplyDemandRecordDto
                    {
                        Type = SupplyDemandType.Supply,
                        DocNo = line.Document.Code,
                        DocType = "采购订单",
                        DocTypeName = "采购订单",
                        BusinessDate = businessDate.ToString("yyyy-MM-dd"),
                        Quantity = line.Quantity,
                        CompletedQuantity = line.Quantity - line.RemainingQuantity,
                        PendingQuantity = line.RemainingQuantity,
                        Status = GetDocumentStatusDisplayName(line.Document.DocumentStatus),
                        SourceId = line.ItemMasterId,
                        SourceLineId = line.Id,
                        ItemCode = line.ItemCode,
                        ItemName = line.ItemName,
                        ItemSpec = line.ItemSpec,
                    }
                );
            }

            // 5. 请购单
            var purchaseRequisitionLines =
                await PurchaseRequisitionRepository.GetChildrenPagedListAsync(
                    x =>
                        x.Document.IsDeleted == false
                        && x.DeliveryDate.HasValue
                        && x.Document.DocumentStatus != DocumentStatus.Closed
                        && x.RemainingQuantity
                            > 0 // 只统计未完成的数量
                    ,
                    0,
                    int.MaxValue,
                    null,
                    true
                );

            foreach (var line in purchaseRequisitionLines)
            {
                supplyRecords.Add(
                    new SupplyDemandRecordDto
                    {
                        Type = SupplyDemandType.Supply,
                        DocNo = line.Document.Code,
                        DocType = "请购单",
                        DocTypeName = "请购单",
                        BusinessDate = line.DeliveryDate.Value.ToString("yyyy-MM-dd"),
                        Quantity = line.Quantity,
                        CompletedQuantity = line.Quantity - line.RemainingQuantity,
                        PendingQuantity = line.RemainingQuantity,
                        Status = GetDocumentStatusDisplayName(line.Document.DocumentStatus),
                        SourceId = line.ItemMasterId,
                        SourceLineId = line.Id,
                        ItemCode = line.ItemCode,
                        ItemName = line.ItemName,
                        ItemSpec = line.ItemSpec,
                    }
                );
            }

            return supplyRecords;
        }
    }
}
